package com.mazaiting.xspring.xservlet;

import com.mazaiting.xspring.xannotation.XAutowired;
import com.mazaiting.xspring.xannotation.XController;
import com.mazaiting.xspring.xannotation.XRequestMapping;
import com.mazaiting.xspring.xannotation.XService;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;

/***
 *
 *
 *                                                    __----~~~~~~~~~~~------___
 *                                   .  .   ~~//====......          __--~ ~~
 *                   -.            \_|//     |||\\  ~~~~~~::::... /~
 *                ___-==_       _-~o~  \/    |||  \\            _/~~-
 *        __---~~~.==~||\=_    -_--~/_-~|-   |\\   \\        _/~
 *    _-~~     .=~    |  \\-_    '-~7  /-   /  ||    \      /
 *  .~       .~       |   \\ -_    /  /-   /   ||      \   /
 * /  ____  /         |     \\ ~-_/  /|- _/   .||       \ /
 * |~~    ~~|--~~~~--_ \     ~==-/   | \~--===~~        .\
 *          '         ~-|      /|    |-~\~~       __--~~
 *                      |-~~-_/ |    |   ~\_   _-~            /\
 *                           /  \     \__   \/~                \__
 *                       _--~ _/ | .-~~____--~-/                  ~~==.
 *                      ((->/~   '.|||' -_|    ~~-/ ,              . _||
 *                                 -_     ~\      ~~---l__i__i__i--~~_/
 *                                 _-~-__   ~)  \--______________--~~
 *                               //.-~~~-~_--~- |-------~~~~~~~~
 *                                      //.-~~~--\
 *                               神兽保佑
 *                              代码无BUG!
 * @author mazaiting
 * @date 2019-08-26
 * @decription 分发器
 */
public class XDispatchServlet extends HttpServlet {

    /**
     * 属性配置文件
     */
    private Properties mContextConfig = new Properties();
    /**
     * 类型列表
     */
    private List<String> mClassNameList = new ArrayList<>();

    /**
     * IOC 容器
     */
    Map<String, Object> mIocMap = new HashMap<>();

    Map<String, Method> mHandlerMapping = new HashMap<>();

    @Override
    public void init(ServletConfig servletConfig) throws ServletException {
        super.init(servletConfig);
        //1、加载配置文件
        doLoadConfig(servletConfig.getInitParameter("contextConfigLocation"));

        //2、扫描相关的类
        doScanner(mContextConfig.getProperty("scan-package"));

        //3、初始化 IOC 容器，将所有相关的类实例保存到 IOC 容器中
        doInstance();

        //4、依赖注入
        doAutowired();

        //5、初始化 HandlerMapping
        initHandlerMapping();

        System.out.println("XSpring FrameWork is init.");

        //6、打印数据
        doTestPrintData();
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

        try {
            doDispatch(req, resp);
        } catch (Exception e) {
            e.printStackTrace();
            resp.getWriter().write("500 Exception Detail:\n" + Arrays.toString(e.getStackTrace()));
        }


    }

    /**
     * 1. 加载配置文件
     * 配置路径: web.xml --> servlet/init-param
     *
     * @param name 配置文件名
     */
    private void doLoadConfig(String name) {
        // 加载配置文件
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(name);

        try {
            // 加载到内存
            mContextConfig.load(inputStream);
            System.out.println("[INFO-1] property file has been saved in contextConfig.");
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != inputStream) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 2. 扫描相关类
     * 配置路径: properties --> scan-package
     *
     * @param packageName 包名
     */
    private void doScanner(String packageName) {
        // 获取类路径
        URL resourcePath = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));
        // 检测是否为空
        if (null == resourcePath) return;
        // 创建类文件夹
        File classPath = new File(resourcePath.getFile());
        // 获取类文件
        File[] listFiles = classPath.listFiles();
        // 检测是否为空
        if (null != listFiles) {
            // 遍历
            for (File file : listFiles) {
                // 检测文件是否为路径
                if (file.isDirectory()) {

                    System.out.println("[INFO-2] {" + file.getName() + "} is a directory.");

                    // 子目录递归
                    doScanner(packageName + "." + file.getName());

                } else {
                    // 检测文件是否为 class文件
                    if (!file.getName().endsWith(".class")) {
                        System.out.println("[INFO-2] {" + file.getName() + "} is not a class file.");
                        continue;
                    }

                    String className = (packageName + "." + file.getName()).replace(".class", "");

                    // 保存在内容
                    mClassNameList.add(className);

                    System.out.println("[INFO-2] {" + className + "} has been saved in classNameList.");
                }
            }
        }
    }

    /**
     * 3. 初始化 IOC 容器，将所有相关的类实例保存到 IOC 容器中
     */
    private void doInstance() {
        // 检测类类别是否为空
        if (mClassNameList.isEmpty()) return;
        try {
            // 遍历类名列表
            for (String className : mClassNameList) {
                // 加载类
                Class<?> clazz = Class.forName(className);
                if (clazz.isAnnotationPresent(XController.class)) {
                    // 检测是否有控制器注解
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    // 创建实例
                    Object instance = clazz.newInstance();
                    // 保存在 ioc 容器
                    mIocMap.put(beanName, instance);
                    System.out.println("[INFO-3] {" + beanName + "} has been saved in iocMap.");
                } else if (clazz.isAnnotationPresent(XService.class)) {
                    // 检测是否有服务注解
                    // 获取实例名
                    String beanName = toLowerFirstCase(clazz.getSimpleName());
                    // 如果注解包含自定义名称
                    XService xService = clazz.getAnnotation(XService.class);
                    // 获取值
                    if (!"".equals(xService.value())) {
                        beanName = xService.value();
                    }
                    // 创建实例
                    Object instance = clazz.newInstance();
                    // 保存
                    mIocMap.put(beanName, instance);
                    System.out.println("[INFO-3] {" + beanName + "} has been saved in iocMap.");

                    // 遍历找类的接口
                    for (Class<?> i : clazz.getInterfaces()) {
                        // 检测是否包含
                        if (mIocMap.containsKey(i.getName())) {
                            throw new Exception("The Bean Name Is Exist.");
                        }
                        // 保存
                        mIocMap.put(i.getName(), instance);
                        System.out.println("[INFO-3] {" + i.getName() + "} has been saved in iocMap.");
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 4、依赖注入
     */
    private void doAutowired() {
        // 检测是否为空
        if (mIocMap.isEmpty()) return;
        // 遍历
        for (Map.Entry<String, Object> entry : mIocMap.entrySet()) {
            // 获取属性
            Field[] fields = entry.getValue().getClass().getDeclaredFields();
            // 迭代
            for (Field field : fields) {
                // 检测是否有自动注入
                if (!field.isAnnotationPresent(XAutowired.class)) {
                    continue;
                }

                System.out.println("[INFO-4] Existence XAutowired.");

                // 获取注解对应的类
                XAutowired xAutowired = field.getAnnotation(XAutowired.class);
                // 获取实例名
                String beanName = xAutowired.value().trim();

                // 获取 XAutowired 注解的值
                if ("".equals(beanName)) {
                    System.out.println("[INFO] xAutowired.value() is null");
                    beanName = field.getType().getName();
                }

                // 只要加了注解，都要加载，不管是 private 还是 protect
                field.setAccessible(true);

                try {
                    // 设置
                    field.set(entry.getValue(), mIocMap.get(beanName));

                    System.out.println("[INFO-4] field set {" + entry.getValue() + "} - {" + mIocMap.get(beanName) + "}.");
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 5. 初始化 HandlerMapping
     */
    private void initHandlerMapping() {
        // 检测 IOC 是否为空
        if (mIocMap.isEmpty()) return;

        // 遍历 IOC
        for (Map.Entry<String, Object> entry : mIocMap.entrySet()) {
            // 获取值
            Class<?> clazz = entry.getValue().getClass();
            // 检测是否有控制器注解
            if (!clazz.isAnnotationPresent(XController.class)) {
                continue;
            }
            // 基地址
            String baseUrl = "";
            // 检测类上的 XRequestMapping 注解
            if (clazz.isAnnotationPresent(XRequestMapping.class)) {
                // 获取注解
                XRequestMapping xRequestMapping = clazz.getAnnotation(XRequestMapping.class);
                // 获取值
                baseUrl = xRequestMapping.value();
            }
            // 遍历类方法
            for (Method method : clazz.getMethods()) {
                // 检测方法是否有 XRequestMapping 注解
                if (!method.isAnnotationPresent(XRequestMapping.class)) {
                    continue;
                }
                // 获取注解
                XRequestMapping xRequestMapping = method.getAnnotation(XRequestMapping.class);
                // 获取请求地址
                String url = ("/" + baseUrl + "/" + xRequestMapping.value()).replaceAll("/+", "/");
                // 添加
                mHandlerMapping.put(url, method);

                System.out.println("[INFO-5] handlerMapping put {" + url + "} - {" + method + "}.");

            }
        }
    }

    /**
     * 6、打印数据
     */
    private void doTestPrintData() {

        System.out.println("[INFO-6]----data------------------------");

        System.out.println("contextConfig.propertyNames()-->" + mContextConfig.propertyNames());

        System.out.println("[classNameList]-->");
        for (String str : mClassNameList) {
            System.out.println(str);
        }

        System.out.println("[iocMap]-->");
        for (Map.Entry<String, Object> entry : mIocMap.entrySet()) {
            System.out.println(entry);
        }

        System.out.println("[handlerMapping]-->");
        for (Map.Entry<String, Method> entry : mHandlerMapping.entrySet()) {
            System.out.println(entry);
        }

        System.out.println("[INFO-6]----done-----------------------");

        System.out.println("====启动成功====");
        System.out.println("测试地址：http://localhost:8080/test/query?username=mazaiting");
        System.out.println("测试地址：http://localhost:8080/test/listClassName");
    }

    /**
     * 7、运行阶段，进行拦截，匹配
     * 分发请求
     *
     * @param req  请求
     * @param resp 响应
     */
    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws InvocationTargetException, IllegalAccessException {
        // 获取请求 URI
        String url = req.getRequestURI();
        // 获取上下文路径
        String contextPath = req.getContextPath();
        // 替换 URL 中的特殊字符
        url = url.replaceAll(contextPath, "").replaceAll("/+", "/");

        System.out.println("[INFO-7] request url-->" + url);
        // 检测是否包含地址
        if (!mHandlerMapping.containsKey(url)) {
            try {
                resp.getWriter().write("404 NOT FOUND!!");
                return;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        // 获取方法
        Method method = mHandlerMapping.get(url);

        System.out.println("[INFO-7] method-->" + method);

        // 获取实例名
        String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());

        System.out.println("[INFO-7] iocMap.get(beanName)->" + mIocMap.get(beanName));

        // 第一个参数是获取方法，后面是参数，多个参数直接加，按顺序对应
        method.invoke(mIocMap.get(beanName), req, resp);

        System.out.println("[INFO-7] method.invoke put {" + mIocMap.get(beanName) + "}.");
    }

    /**
     * 获取类的首字母小写的名称
     *
     * @param className 类名
     * @return 实例名
     */
    private String toLowerFirstCase(String className) {
        char[] charArray = className.toCharArray();
        charArray[0] += 32;
        return String.valueOf(charArray);
    }
}
